package webtrans

import (
	"fmt"
	"gitee.com/DreamPublic/signal/pkg/proto"
	"gitee.com/DreamPublic/signal/utils"
	"go.uber.org/zap"
	"net"
	"net/http"
	"nhooyr.io/websocket"
	"sync"
	"time"
)

type Signal struct {
	connectCbs []proto.SignalCallback
	closeCbs   []proto.SignalCallback
	authCb     proto.AuthCallback
	cbLock     sync.Mutex
	// sessionId - *Session
	sessionMap sync.Map
	certFile   string
	keyFile    string
	port       int
}

func NewSignal(port int, certFile, keyFile string) *Signal {
	return &Signal{
		connectCbs: make([]proto.SignalCallback, 0),
		closeCbs:   make([]proto.SignalCallback, 0),
		authCb:     nil,
		cbLock:     sync.Mutex{},
		sessionMap: sync.Map{},
		certFile:   certFile,
		keyFile:    keyFile,
		port:       port,
	}
}

func (s *Signal) OnConnect(cb proto.SignalCallback) {
	s.cbLock.Lock()
	defer s.cbLock.Unlock()
	s.connectCbs = append(s.connectCbs, cb)
}

func (s *Signal) OnClose(cb proto.SignalCallback) {
	s.cbLock.Lock()
	defer s.cbLock.Unlock()
	s.closeCbs = append(s.closeCbs, cb)
}

func (s *Signal) OnAuth(cb proto.AuthCallback) {
	s.cbLock.Lock()
	defer s.cbLock.Unlock()
	s.authCb = cb
}

func (s *Signal) fireConnectCb(session *Session) {
	for _, cb := range s.connectCbs {
		cb(session)
	}
}

func (s *Signal) fireCloseCb(session *Session) {
	s.sessionMap.Delete(session.id)
	for _, cb := range s.closeCbs {
		go cb(session)
	}
}

func (s *Signal) fireAuthCb(info proto.AuthCbStruct) bool {
	if nil == s.authCb {
		return true
	} else {
		return s.authCb(info)
	}
}

func (s *Signal) Run() error {
	log := zap.L()
	listen, listenErr := net.Listen("tcp", fmt.Sprintf(":%d", s.port))
	if nil != listenErr {
		log.Error("初始化监听出错", zap.Error(listenErr))
		return listenErr
	}
	httpServer := &http.Server{
		Handler:      NewSignalHandler(s),
		ReadTimeout:  time.Second,
		WriteTimeout: time.Second,
	}
	if len(s.certFile) < 1 || len(s.keyFile) < 1 {
		return httpServer.Serve(listen)
	} else {
		return httpServer.ServeTLS(listen, s.certFile, s.keyFile)
	}
}

type SignalHandler struct {
	signal *Signal
}

func NewSignalHandler(signal *Signal) *SignalHandler {
	return &SignalHandler{signal: signal}
}

func (sh *SignalHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	log := zap.L()
	con, acceptErr := websocket.Accept(w, r, &websocket.AcceptOptions{
		OriginPatterns: []string{"*"},
	})
	if nil != acceptErr {
		log.Error("accept con 失败", zap.Error(acceptErr))
		return
	}
	sessionId := utils.UUID()
	log = log.With(zap.String("sessionId", sessionId))
	session := NewSession(sessionId, con, sh.signal)
	sh.signal.sessionMap.Store(sessionId, session)
	log.Info("accept session success")
	// 处理后续逻辑
	go session.Handle(r)
}
